#pragma once
#ifndef SETTINGS__ITEM__INCLUDED__HH__
#define SETTINGS__ITEM__INCLUDED__HH__

#include <qtablewidget.h>
#include <qlabel.h>
#include <qdial.h>
#include <qlineedit.h>
#include <qcombobox.h>
#include <qcolordialog.h>
#include <qmessagebox.h>
#include <qcheckbox.h>
#include <qevent.h>
#include <qboxlayout.h>
#include <qpushbutton.h>
#include <LitString.h>
#include <LitMovableTypes.h>

class SettingsItem : public QWidget
{
	Q_OBJECT
protected:
	std::string mName,
				mValue;
	bool		mModifiable;
public:
	SettingsItem( QWidget * parent );
	SettingsItem( const std::string &name, const std::string &value, bool modifiable, QWidget * parent );
	virtual ~SettingsItem();

	void setModifiable( bool state );
	bool isModifiable() const;

	const std::string &getName() const;
	const std::string &value() const;
	
	virtual void reloadSetting( ) = 0;
	virtual void saveSetting( ) = 0;

	QDialog::DialogCode askSaveChanges()
	{
		if( QMessageBox::question( this, "Property Changed", "Do you want to accept or ignore changes ?", QMessageBox::Save,QMessageBox::Ignore ) == QMessageBox::Save )
			return QDialog::Accepted;
		return QDialog::Rejected;
	}
signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};
template<typename T>
class SettingsItemWidget : public SettingsItem
{
protected:
	T* mInst;
public:
	SettingsItemWidget( const std::string &name, T * instance, bool modifiable = true, const char * changed_member_func = NULL, QObject* rcvr = NULL, const char * apply_change_func = NULL )
		: mInst( instance )
		, SettingsItem(name, "", modifiable, instance->parentWidget())
	{
		setLayout( new QVBoxLayout(this) );
		layout()->addWidget( instance );
		if(changed_member_func)
		{
			connect( instance, changed_member_func, rcvr, apply_change_func );
		}
		instance->setEnabled(modifiable);
		instance->setContentsMargins( 0, 0, 0, 0 );
		layout()->setContentsMargins( 0, 0, 0, 0 );
	}
	~SettingsItemWidget(){}
	
	void reloadSetting( ){ }
	void saveSetting( ){}

	T* instance(){ return mInst; }
};

class SettingsTextItem : public SettingsItem
{
	Q_OBJECT
	QLineEdit *		mLabel;
	QVBoxLayout *	mLay;

	void _create( const std::string &val, bool modifiable );
public:
	SettingsTextItem( const std::string &name, const std::string &value, bool modifiable, QWidget * parent ) ;
	virtual ~SettingsTextItem();
	
	void reloadSetting( ){ mLabel->setText( mValue.c_str() ); }
	void saveSetting( ){ mValue = mLabel->text().toAscii().data(); }

	void setValue( const std::string &str ){ mValue = str; if( mLabel ) mLabel->setText( str.c_str() ); }
	std::string value() const{ if( mLabel ) return mLabel->text().toAscii().data(); return ""; }
	bool eventFilter( QObject * obj, QEvent * evt );
signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};

class SettingsTable;
class SettingsTableItem : public SettingsItem
{
	Q_OBJECT

	SettingsTable*	mTable;
	QHBoxLayout*	mLay;
	void _create( const std::string &val, bool modifiable );
public:
	SettingsTableItem( const std::string &name, const std::string &value, bool modifiable, QWidget * parent ) ;
	virtual ~SettingsTableItem();
	
	void reloadSetting( );
	void saveSetting( );

	SettingsTable* table();
	const SettingsTable* table() const;

signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};


class SettingsComboItem : public SettingsItem
{
	Q_OBJECT
	QComboBox *		mCombo;
	QHBoxLayout *	mLay;
	Lit::StringList mListValue;
	void _create( const Lit::StringList &val, bool modifiable );

	protected slots:
		void _textChanged( const QString &t );
public:
	SettingsComboItem( const std::string &name, const Lit::StringList &value, bool modifiable, QWidget * parent ) ;
	virtual ~SettingsComboItem();
	
	void setValue( const Lit::StringList &lst ){ 
		mListValue = lst; 
		if( !lst.empty() && lst.defaultString() < lst.count() ) mValue = lst.at(lst.defaultString()).str();
		mCombo->clear();
		for( auto i=lst.begin(); i!=lst.end(); i++ )
			mCombo->addItem( QString( i->cstr() ) );
		mCombo->setCurrentIndex(lst.defaultString());
		reloadSetting();
	}
	std::string value() const{ return mCombo->currentText().toAscii().data(); }
	const Lit::StringList& listValue() const{ return mListValue; }
	Lit::StringList& listValue(){ return mListValue; }
	void emitItemChanged(){ saveSetting(); emit itemChanged( mName, mValue ); }
	void reloadSetting( ){ mCombo->setCurrentIndex( mListValue.findId( mValue ) ); }
	void saveSetting( ){ mValue = mCombo->currentText().toAscii().data(); mListValue.setDefaultString(mValue); }
signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};


class SettingsCheckboxItem : public SettingsItem
{
	Q_OBJECT
	QCheckBox *		mCheck;
	QVBoxLayout *	mLay;
	void _create( bool val, bool modifiable );

	protected slots:
		void _valueChanged( bool v );
public:
	SettingsCheckboxItem( const std::string &name, bool value, bool modifiable, QWidget * parent ) ;
	virtual ~SettingsCheckboxItem();

	void reloadSetting( ){ bool v = Ogre::StringConverter::parseBool(mValue); mCheck->setChecked(v); }
	void saveSetting( ){ mValue = Ogre::StringConverter::toString( (bool)(mCheck->isChecked()) ); }
	void setValue( bool state ){ mCheck->setChecked( state ); mValue = Ogre::StringConverter::toString(state); }
	bool value() const{ return mCheck->isChecked(); }
signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};


class SettingsVectorItem : public SettingsItem
{
	Q_OBJECT
public:
	class Component : public QWidget
	{
	protected:
		QHBoxLayout *	mLayout;
		QLabel*			mHeader;
		QLineEdit*		mEdit;
	public:
		Component( const std::string &caption, Ogre::Real value, QWidget*parent )
			: QWidget( parent )
		{
			mLayout = new QHBoxLayout( this );
			setLayout( mLayout );

			mHeader = new QLabel( caption.c_str(), this );
			mEdit = new QLineEdit( this );
			mEdit->setText( QString::number(value) );
			mEdit->setValidator( new QDoubleValidator( -1000000, 1000000, 4, this ) );
			mLayout->addWidget( mHeader );
			mLayout->addWidget( mEdit );

			mEdit->installEventFilter(parent);
			setMinimumHeight(20);
			mEdit->setMaximumWidth( 50 );
			mLayout->setContentsMargins( 0,0,0,0 );
		}
		~Component( ){}

		Ogre::Real value() const{ return mEdit->text().toFloat(); }
		std::string valueStr() const{ return mEdit->text().toAscii().data(); }
		void setValue( const std::string &str ){ mEdit->setText(str.c_str()); }
		void setValue( Ogre::Real num ){ mEdit->setText(QString::number(num)); }
	};
	std::vector<Component*>		mComponents;
	QBoxLayout *				mLay;

	void _create( const Ogre::Vector3& val, bool modifiable, bool inline_ );

protected slots:
public:
	SettingsVectorItem( const std::string &name, const Ogre::Vector3& value, bool modifiable, bool inline_, QWidget * parent ) ;
	virtual ~SettingsVectorItem();
	

	void reloadSetting( ){ 
		Ogre::Vector3 v = Ogre::StringConverter::parseVector3( mValue );
		mComponents[0]->setValue( v.x );
		mComponents[1]->setValue( v.y );
		mComponents[2]->setValue( v.z );
	}
	void saveSetting( ){ 
		mValue = Ogre::StringConverter::toString( value() ); 
	}
	void setValue( const Ogre::Vector3 &v ){ 
		mValue = Ogre::StringConverter::toString( v ); 
		mComponents[0]->setValue( v.x );
		mComponents[1]->setValue( v.y );
		mComponents[2]->setValue( v.z );
	}
	Ogre::Vector3 value() const;

	bool eventFilter( QObject*obj, QEvent*evt );
signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};


class SettingsDegreeItem : public SettingsItem
{
	Q_OBJECT
	QDial *			mDial;
	QLabel *		mLabel;
	QVBoxLayout *	mLay;

	void _create( const Ogre::Degree &val, bool modifiable );
public:
	SettingsDegreeItem( const std::string &name, const Ogre::Degree &value, bool modifiable, QWidget * parent ) ;
	virtual ~SettingsDegreeItem();
	
	void setValue( const Ogre::Degree &deg );
	Ogre::Degree value() const;

	void reloadSetting( ){ 
		mDial->setSliderPosition( Ogre::StringConverter::parseAngle( mValue ).valueDegrees() );
	}
	void saveSetting( ){ 
		mValue = Ogre::StringConverter::toString( Ogre::Degree(mDial->sliderPosition()) );
	}
protected slots:
	void _valueChanged( int v );

signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};
class SettingsQuaternionItem : public SettingsItem
{
	Q_OBJECT

protected:
	QDial*					mAngle;
	QComboBox*				mAxis;
	QLabel*					mAngleValue;
	SettingsVectorItem*		mCustomAxis;
	QVBoxLayout*			mLayout;
	Lit::Quat				mQuat;

	void _create(const std::string &name, const Lit::Quat& value, bool modifiable);
public:
	SettingsQuaternionItem(const std::string &name, const Lit::Quat& value, bool modifiable, QWidget*parent ) ;
	~SettingsQuaternionItem();
	
	Ogre::Degree		axisAngle() const;
	Lit::AxisDirection	axisType() const;
	Lit::Vec3			axis() const;

	bool isCustomAxis() const;

	
	void setValue( const Lit::Quat &str ){ 
		mQuat = str;
		mAxis->setCurrentIndex(Lit::AXIS_CUSTOM);
		mCustomAxis->setValue( quatAxis(mQuat) );
		mAngle->setValue( quatAngle(mQuat).valueDegrees() );
		mValue = Ogre::StringConverter::toString(mQuat);
	}
	Lit::Quat value() const{
		Lit::Quat ret = Lit::Quat( Ogre::Degree(mAngle->value()), axis() );
	}

	Lit::Vec3		quatAxis( Lit::Quat & q ) const;
	Ogre::Degree	quatAngle( Lit::Quat & q ) const;

	void reloadSetting( );
	void saveSetting( );

protected slots:
	void angleChanged( int a );
	void changeAxis( const QString &ax );
signals:
	void itemChanged( const std::string &_nam, const std::string &_val );

};
struct QColorLabel : public QLabel
{
	Q_OBJECT
protected:
	QColor			mColor;

public:
	QColorLabel( const Ogre::ColourValue &value, QWidget*w );
	QColorLabel( const QColor &value, QWidget*w );
	~QColorLabel();

	void setColor( const QColor& col );
	void setColor( const Ogre::ColourValue& col );
	
	
	Ogre::ColourValue		ogreColor() const;
	const QColor&			qColor() const;
	void mouseReleaseEvent( QMouseEvent * evt );

protected slots:
	void _openDialog( );
signals:
	void colorChanged( const QColor & );
	
};
class SettingsColorItem : public SettingsItem
{
	Q_OBJECT

	QColorLabel *		mLabel;
	QVBoxLayout *	mLay;

	void _create( const Ogre::ColourValue &val, bool modifiable );
public:
	SettingsColorItem( const std::string &name, const Ogre::ColourValue &value, bool modifiable, QWidget * parent ) ;
	SettingsColorItem( const std::string &name, const QColor &value, bool modifiable, QWidget * parent ) ;
	virtual ~SettingsColorItem();
	void setValue( const Lit::Color &col ){ 
		setColor(col);
	}
	Lit::Color value() const{
		return ogreColor();
	}
	void reloadSetting( ){ 
		setColor( Ogre::StringConverter::parseColourValue(mValue) );
	}
	void saveSetting( ){ 
		mValue = Ogre::StringConverter::toString( ogreColor() );
	}
	void setColor( const QColor& col );
	void setColor( const Ogre::ColourValue& col );

	Ogre::ColourValue		ogreColor() const;
	const QColor&			qColor() const;
protected slots:
	void _colorChanged( const QColor &col );
signals:
	void itemChanged( const std::string &_nam, const std::string &_val );
};

#endif